/*
 * SocketUDP.cpp
 *
 *  Created on: 11.04.2011
 *      Author: mifodix
 */

#include "SocketUDP.hpp"

int SocketUDP::SendAll(void *packet, size_t packetSize, const char* toAddress, uint16_t port)
{
    if (packet==NULL)
    {
    	Logger::Log("SocketUDP", STATE_WARNING, "Packet is NULL.");
    }
    if (toAddress==NULL)
    {
    	Logger::Log("SocketUDP", STATE_ERROR, "Address to send is empty!");
    	return -1;
    }
	sockaddr_in to;
    in_addr address;
    if (inet_pton(AF_INET,toAddress, &address)<=0)
    {
    	Logger::Log("SocketUDP", STATE_ERROR, "Can't convert address to send!");
    	return -1;
    }
    to.sin_family=AF_INET;
    to.sin_port=htons(port);
    to.sin_addr.s_addr=address.s_addr;
    socklen_t toLen=sizeof(to);
	size_t total = 0;
    int n;
    while (total < packetSize)
    {
        n=sendto(mySocket, ((char *) packet)+total, packetSize-total, 0, (sockaddr*) &to, toLen);
        if (n == SOCKET_ERROR)
        {
        	Logger::Log("SocketUDP", STATE_WARNING, "Sending data is failed.");
        	break;
        }
        total += n;
    }
    return (n==-1 ? -1 : total);
}

int SocketUDP::ReceiveAll(void *packet, size_t packetSize, char* fromAddress, uint16_t &port)
/* нужно точно указывать размер пакета!*/
{
	if (!packet)
	{
		Logger::Log("SocketUDP", STATE_ERROR, "Packet is NULL!");
		return -1;
	}
	sockaddr_in from;
    socklen_t fromLen=sizeof(from);
	size_t total=0;
	int n;
	while (total<packetSize)
	{
		n=recvfrom(mySocket, ((char *) packet)+total, packetSize-total, 0, (sockaddr*) &from, &fromLen);
		if (n==SOCKET_ERROR)
		{
			Logger::Log("SocketUDP", STATE_WARNING, "Recieving data is failed.");
			break;
		}
		total+=n;
	}
	if (fromAddress)
	{
		strcpy(fromAddress, inet_ntoa(from.sin_addr));
	}
	port=ntohs(from.sin_port);
	return (n==-1 ? -1 : total);
}

bool SocketUDP::Bind(uint32_t bindAddress, uint16_t port, bool isBroadcast)
{
	if (isBounded)//на случай пересвязывания
	{
		CLOSE_SOCKET(mySocket);
		isBounded=false;
		Logger::Log("SocketUDP", STATE_INFO, "Socket unbounded.");
	}
	mySocket = socket(AF_INET, SOCK_DGRAM, 0);//создаём сокет для udp
	if (mySocket==INVALID_SOCKET)//сокет не создался
	{
		Logger::Log("SocketUDP", STATE_ERROR, "Can't create socket!");
		return false;
	}
	if (isBroadcast)
	{
		SOCK_OPTION_TYPE enableBroadcast = 1;
		this->isBroadcast = isBroadcast;
		if (setsockopt(mySocket, SOL_SOCKET, SO_BROADCAST, &enableBroadcast, sizeof(enableBroadcast)) < 0)
		{
			Logger::Log("SocketUDP", STATE_WARNING, "Can't turn on broadcast option to socket.");
			CLOSE_SOCKET(mySocket);
			return false;
		}
	}
	myAddress.sin_family = AF_INET;
	myAddress.sin_port = htons(port);//вешаем на порт
	myAddress.sin_addr.s_addr = htonl(bindAddress);
	if (bind(mySocket, (sockaddr*) &myAddress, sizeof(myAddress)) == SOCKET_ERROR)
	{
		Logger::Log("SocketUDP", STATE_ERROR, "Can't bind socket!");
		CLOSE_SOCKET(mySocket);
		return false;
	}
	isBounded=true;
	Logger::Log("SocketUDP", STATE_INFO, "Socket was successfully created.");
	return true;
}

bool SocketUDP::Bind(const char *bindAddress, uint16_t port, bool isBroadcast)
{
	if (bindAddress == NULL)
	{
		Logger::Log("SocketUDP", STATE_ERROR, "Bind address is empty!");
		return false;
	}
	uint32_t add;
	if (inet_pton(AF_INET,bindAddress, &add) <= 0)
	{
		Logger::Log("SocketUDP", STATE_ERROR, "Can't convert string to IP-address!");
		return false;
	}
	return Bind(add, port, isBroadcast);
}

int SocketUDP::Receive(void *packet, size_t packetSize, char *fromAddress, uint16_t & port)
/*заданные размер пакета может быть больше реального размера пакета*/
{
	if (!packet)
	{
		Logger::Log("SocketUDP", STATE_ERROR, "Packet is NULL!");
		return -1;
	}
	sockaddr_in from;
    socklen_t fromLen=sizeof(from);
    int n=recvfrom(mySocket, (char*) packet, packetSize, 0, (sockaddr*) &from, &fromLen);
	if (n==SOCKET_ERROR)
	{
		Logger::Log("SocketUDP", STATE_WARNING, "Recieving data is failed.");
	}
	if (fromAddress)
	{
		strcpy(fromAddress, inet_ntoa(from.sin_addr));
	}
	port=ntohs(from.sin_port);
	return (n==-1 ? -1 : n);
}

SocketUDP::~SocketUDP()
{
	if (isBounded)
	{
		CLOSE_SOCKET(mySocket);
		Logger::Log("SocketUDP", STATE_INFO, "Socket was closed.");
	}
	FINISH();
}
